In this example we will use the IPUMS USA data to produce survey-based estimates for various geographic levels present in the IPUMS. This example uses the 2014-2018 ACS 5-year microdata.

The good folks at IPUMS have created a library to read in their data from the .gz file that you download. Be sure you right click and save the DDI codebook when you create your extract

This will save the xml file that contains all the information on the data (what is contained in the data file) to your computer. When using IPUMS, it will have a name like usa_xxxxx.xml where the x’s represent the extract number (I’m on 92 as of today).

You will also need to download the data file, by right clicking the Download.DAT link in the above image. This will save a .gz file to your computer, again with a name like: usa_xxxxx.dat.gz. Make sure this file and the xml file from above are in the same folder, preferably your class folder.

Be sure the ipumsr package is installed.

library(ipumsr)
ddi <- read_ipums_ddi("C:/Users/ozd504//OneDrive - University of Texas at San Antonio//gis_classwork/usa_00083.xml")
data <- read_ipums_micro(ddi)
## Use of data from IPUMS USA is subject to conditions including that users should
## cite the data appropriately. Use command `ipums_conditions()` for more details.
data<-haven::zap_labels(data) #necessary to avoid problems with "labelled" data class
names(data)<-tolower(names(data))

Load some other packages

library(survey, quietly = T)
## Warning: package 'Matrix' was built under R version 4.1.3
## Warning: package 'survival' was built under R version 4.1.3
library(tidyverse, quietly = T)
library(car, quietly = T)
library(ggplot2, quietly = T)
library(tigris, quietly = T)
library(classInt, quietly = T)
library(tmap, quietly = T)

Download geographic data for Public Use Microdata Areas

The Public Use Microdata Area is the lowest level of geography in the PUMS data. They correspond to greographic ares of ~ 100,000 people.

options(tigris_class = "sf")
pumas<-pumas(state = "TX",
             year = 2018,
             cb = T)
## 
  |                                                                            
  |                                                                      |   0%
  |                                                                            
  |==                                                                    |   3%
  |                                                                            
  |===                                                                   |   4%
  |                                                                            
  |====                                                                  |   6%
  |                                                                            
  |=====                                                                 |   7%
  |                                                                            
  |=======                                                               |  10%
  |                                                                            
  |========                                                              |  11%
  |                                                                            
  |=========                                                             |  13%
  |                                                                            
  |==========                                                            |  14%
  |                                                                            
  |============                                                          |  17%
  |                                                                            
  |=============                                                         |  18%
  |                                                                            
  |==============                                                        |  20%
  |                                                                            
  |===============                                                       |  22%
  |                                                                            
  |==================                                                    |  25%
  |                                                                            
  |===================                                                   |  27%
  |                                                                            
  |====================                                                  |  29%
  |                                                                            
  |======================                                                |  31%
  |                                                                            
  |=======================                                               |  32%
  |                                                                            
  |========================                                              |  35%
  |                                                                            
  |=========================                                             |  36%
  |                                                                            
  |===========================                                           |  38%
  |                                                                            
  |============================                                          |  39%
  |                                                                            
  |=============================                                         |  42%
  |                                                                            
  |==============================                                        |  43%
  |                                                                            
  |================================                                      |  45%
  |                                                                            
  |=================================                                     |  46%
  |                                                                            
  |===================================                                   |  50%
  |                                                                            
  |=====================================                                 |  54%
  |                                                                            
  |=======================================                               |  56%
  |                                                                            
  |========================================                              |  57%
  |                                                                            
  |==========================================                            |  59%
  |                                                                            
  |==========================================                            |  61%
  |                                                                            
  |============================================                          |  63%
  |                                                                            
  |=============================================                         |  64%
  |                                                                            
  |===============================================                       |  67%
  |                                                                            
  |===============================================                       |  68%
  |                                                                            
  |=================================================                     |  70%
  |                                                                            
  |==================================================                    |  71%
  |                                                                            
  |====================================================                  |  74%
  |                                                                            
  |====================================================                  |  75%
  |                                                                            
  |=======================================================               |  78%
  |                                                                            
  |=========================================================             |  82%
  |                                                                            
  |===========================================================           |  84%
  |                                                                            
  |============================================================          |  86%
  |                                                                            
  |==============================================================        |  88%
  |                                                                            
  |==============================================================        |  89%
  |                                                                            
  |================================================================      |  91%
  |                                                                            
  |=================================================================     |  93%
  |                                                                            
  |===================================================================   |  95%
  |                                                                            
  |===================================================================   |  96%
  |                                                                            
  |===================================================================== |  99%
  |                                                                            
  |======================================================================| 100%
plot(pumas["GEOID10"],
     main = "Public Use Microdata Areas in Texas")

mapview::mapview(pumas, zcol= "GEOID10")

Prepare variables

Here I recode several demographic variables

data$pwt <- data$perwt
data$hwt <- data$hhwt

#race/ethnicity
data$hisp <- Recode(data$hispan, recodes = "9=NA; 1:4='Hispanic'; 0='NonHispanic'")
data$race_rec <- Recode(data$race, recodes = "1='White'; 2='Black'; 3='Other'; 4:6='Asian'; 7:9='Other'")
data$race_eth <- interaction(data$hisp, data$race_rec, sep = "_")
data$race_eth  <- as.factor(ifelse(substr(as.character(data$race_eth),1,8) == "Hispanic", "Hispanic", as.character(data$race_eth)))
data$race_eth <- relevel(data$race_eth, ref = "NonHispanic_White")

#sex
data$male <- ifelse(data$sex == 1,1,0)

#education
data$educ_level<- Recode(data$educd, recodes = "2:61='0LT_HS';62:64='1_HSD/GED';65:80='2_somecoll';90:100='2_somecoll'; 81:83='3_AssocDegree';101='4_bachelordegree'; 110:116='4_BAplus_GradDegree'; else=NA")

#employment
data$employed <- Recode(data$wrklstwk, recodes = "1=0;2=1; else=NA")

#citizenship
data$cit<-Recode(data$citizen, recodes = "1='US born'; 2='naturalized'; 3:4='notcitizen';else=NA ")

#industry
data$ind_group<-Recode(data$ind, recodes = "170:490='ag_extract'; 770='construction'; 1070:3990='manufac'; 4070:5790='whole_retail'; 6070:6390='trans'; 6470:6780='information'; 6870:7190= 'fire'; 7270=7790='prof/sci/manage'; 7860:8470='edu/social'; 8560:8690='arts'; 8770:9290='other'; 9370:9590='public_adm'; 9670:9870='military'; else=NA ")

data$proftech <- Recode(data$ind, recodes = "7270:7490=1; 0=NA; else=0")

#age in 10 year intervals
data$agecat<-cut(data$age, breaks = c(0, 18, 20, 30, 40, 50, 65, 120), include.lowest = T)

data$income <- ifelse(data$incwage>=999998, NA, data$incwage)

Generate survey design object

Here we identify the person weights and the survey design variables.

des<-svydesign(ids = ~cluster,
               strata = ~ strata,
               weights = ~pwt,
               data = data)

perform survey estimation for PUMAs

The svyby() function allows us calculate estimates for different sub-domains within the data, this could be a demographic characteristic, but we’ll use our geographic level. Of course you could do both….

test<-svytable(~I(cit=="US born")+puma+sex, design=des )

puma_est_edu<-svyby(formula = ~educ_level,
                    by = ~puma,
                    design = subset(des, age>25),
                    FUN=svymean,
                    na.rm = TRUE )

puma_est_employ<-svyby(formula = ~employed,
                       by = ~puma,
                       design=subset(des, age %in% 18:65),
                       FUN=svymean,
                       na.rm = TRUE )

puma_est_industry<-svyby(formula = ~proftech,
                         by = ~puma+sex+race_eth,
                         design = subset(des, employed==1),
                         FUN = svymean,
                         na.rm = TRUE )
library(reldist)
## Warning: package 'reldist' was built under R version 4.1.3
## reldist: Relative Distribution Methods
## Version 1.7-0 created on 2021-11-10.
## copyright (c) 2003, Mark S. Handcock, University of California-Los Angeles
##  For citation information, type citation("reldist").
##  Type help(package="reldist") to get started.
gini.puma<-data%>%
  filter(income >0, is.na(income)==F)%>%
  group_by( puma, race_eth)%>%
  summarize(ineq = gini(income, weights = pwt))%>%
  ungroup()
## `summarise()` has grouped output by 'puma'. You can override using the
## `.groups` argument.
head(puma_est_edu)
head(puma_est_employ)
head(puma_est_industry)

join to geography

pumas$puma<-as.numeric(pumas$PUMACE10)

geo1<-left_join(pumas, puma_est_employ, by=c("puma"= "puma"))
head(geo1)
geo2<-left_join(pumas, puma_est_industry, by=c("puma"= "puma"))
head(geo2)
geo3<-left_join(pumas, gini.puma, by=c("puma"= "puma"))
head(geo2)

Map estimates

Employment rates by PUMA

tmap_mode("view")
## tmap mode set to interactive viewing
geo1%>%
  tm_shape()+
  tm_polygons("employed",
              style="kmeans",
              n=8,
              legend.hist = TRUE) +
  tm_layout(legend.outside = TRUE,
            title = "Employment rate in Texas PUMAs \n 2014-2018") 
#tmap_mode("plot")
geo2%>%
  tm_shape()+
  tm_polygons("proftech",
              style="kmeans",
              n=8,
              legend.hist = TRUE) +
 tm_layout(legend.outside = TRUE,
            title = "Employment rate in Texas PUMAs \n 2014-2018")  
geo3%>%
tm_shape()+
  tm_polygons("ineq",
              style="kmeans",
              n=8,
              legend.hist = TRUE) +
 tm_layout(legend.outside = TRUE,
            title = "Employment rate in Texas PUMAs \n 2014-2018")  

Estimation for metro areas

Here we use core based statistical areas instead of PUMAs

mets<-core_based_statistical_areas(cb = T, year = 2018)
mets<-mets[grep(mets$NAME,pattern =  "TX"),]
plot(mets["NAME"])

sts<-states(cb=T, year=2018)
sts<-sts%>%
  filter(GEOID==48)

estimates by metro area

met_est_edu<-svyby(formula = ~educ_level,
                   by = ~met2013,
                   design=subset(des,age>25),
                   FUN=svymean,
                   na.rm=T )

met_est_employ<-svyby(formula = ~employed,
                      by = ~met2013,
                      design=subset(des, age%in%18:65),
                      FUN=svymean,
                      na.rm=T )

met_est_industry<-svyby(formula = ~proftech,
                        by = ~met2013,
                        design=subset(des, employed==1),
                        FUN=svymean,
                        na.rm=T )

head(met_est_edu)
head(met_est_employ)
head(met_est_industry)
mets$met2013<-as.numeric(mets$GEOID)
geo3<-left_join(mets, met_est_employ,by=c("met2013"= "met2013"))

Note, grey Metros are ones that are not identified in the ACS

tmap_mode("view")
## tmap mode set to interactive viewing
geo3%>%
  tm_shape()+
  tm_polygons("employed",
              style="kmeans",
              n=8,
              legend.hist = TRUE) +
 tm_layout(legend.outside = TRUE,
            title = "Employment rate in Texas Metro Areas \n 2014-2018")  

Estimation for Counties

cos<-counties(cb= T,state = "TX", year = 2018)
plot(cos["NAME"])

sts<-states(cb=T, year=2018)
sts<-sts%>%
  filter(GEOID==48)

estimates by county area

cos_est_edu<-svyby(formula = ~educ_level,
                   by = ~countyfip,
                   design=subset(des,age>25),
                   FUN=svymean, na.rm=T )
cos_est_employ<-svyby(formula = ~employed,
                      by = ~countyfip,
                      design=subset(des, age%in%18:65),
                      FUN=svymean, na.rm=T )
cos_est_industry<-svyby(formula = ~proftech,
                        by = ~countyfip,
                        design=subset(des, employed==1),
                        FUN=svymean, na.rm=T )

head(cos_est_edu)
head(cos_est_employ)
head(cos_est_industry)

Again, the ACS doesn’t identify counties in the microdata except for those counties with small populations. The list of identified counties can be found here

cos$cofip<-as.numeric(cos$COUNTYFP)


geo4<-left_join(cos, cos_est_employ,by=c("cofip"= "countyfip"))

tmap_mode("view")
## tmap mode set to interactive viewing
geo4%>%
  tm_shape()+
  tm_polygons("employed",
              style="kmeans",
              n=8,
              legend.hist = TRUE) +
 tm_layout(legend.outside = TRUE,
            title = "Employment rate in Texas Counties \n 2014-2018")  
LS0tDQp0aXRsZTogIlVzaW5nIElQVU1TIFVTQSBmb3IgRXN0aW1hdGlvbiBvZiBQb3B1bGF0aW9uIENoYXJhY3RlcmlzdGljcyBpbiBWYXJpb3VzIEdlb2dyYXBoaWMgQXJlYXMiDQphdXRob3I6ICJDb3JleSBTLiBTcGFya3MsIFBoLkQuIC0gVW5pdmVyc2l0eSBvZiBUZXhhcyBhdCBTYW4gQW50b25pbyINCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCLCAlWScpYCINCm91dHB1dDoNCiAgIGh0bWxfZG9jdW1lbnQ6DQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogICAgZmlnX2hlaWdodDogNw0KICAgIGZpZ193aWR0aDogNw0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQotLS0NCg0KSW4gdGhpcyBleGFtcGxlIHdlIHdpbGwgdXNlIHRoZSBbSVBVTVMgVVNBXShodHRwczovL3VzYS5pcHVtcy5vcmcvdXNhLykgZGF0YSB0byBwcm9kdWNlIHN1cnZleS1iYXNlZCBlc3RpbWF0ZXMgZm9yIHZhcmlvdXMgZ2VvZ3JhcGhpYyBsZXZlbHMgcHJlc2VudCBpbiB0aGUgSVBVTVMuIFRoaXMgZXhhbXBsZSB1c2VzIHRoZSAyMDE0LTIwMTggQUNTIDUteWVhciBtaWNyb2RhdGEuIA0KDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQoNClRoZSBnb29kIGZvbGtzIGF0IElQVU1TIGhhdmUgY3JlYXRlZCBhIGxpYnJhcnkgdG8gcmVhZCBpbiB0aGVpciBkYXRhIGZyb20gdGhlIC5neiBmaWxlIHRoYXQgeW91IGRvd25sb2FkLiBCZSBzdXJlIHlvdSByaWdodCBjbGljayBhbmQgc2F2ZSB0aGUgRERJIGNvZGVib29rIHdoZW4geW91IGNyZWF0ZSB5b3VyIGV4dHJhY3QNCg0KIVtdKC4uL2ltYWdlcy9pbXB1bTEucG5nKQ0KDQpUaGlzIHdpbGwgc2F2ZSB0aGUgeG1sIGZpbGUgdGhhdCBjb250YWlucyBhbGwgdGhlIGluZm9ybWF0aW9uIG9uIHRoZSBkYXRhICh3aGF0IGlzIGNvbnRhaW5lZCBpbiB0aGUgZGF0YSBmaWxlKSB0byB5b3VyIGNvbXB1dGVyLiBXaGVuIHVzaW5nIElQVU1TLCBpdCB3aWxsIGhhdmUgYSBuYW1lIGxpa2UgYHVzYV94eHh4eC54bWxgIHdoZXJlIHRoZSB4J3MgcmVwcmVzZW50IHRoZSBleHRyYWN0IG51bWJlciAoSSdtIG9uIDkyIGFzIG9mIHRvZGF5KS4gDQoNCllvdSB3aWxsIGFsc28gbmVlZCB0byBkb3dubG9hZCB0aGUgZGF0YSBmaWxlLCBieSByaWdodCBjbGlja2luZyB0aGUgKipEb3dubG9hZC5EQVQqKiBsaW5rIGluIHRoZSBhYm92ZSBpbWFnZS4gVGhpcyB3aWxsIHNhdmUgYSAuZ3ogZmlsZSB0byB5b3VyIGNvbXB1dGVyLCBhZ2FpbiB3aXRoIGEgbmFtZSBsaWtlOiBgdXNhX3h4eHh4LmRhdC5nemAuIE1ha2Ugc3VyZSB0aGlzIGZpbGUgYW5kIHRoZSB4bWwgZmlsZSBmcm9tIGFib3ZlIGFyZSBpbiB0aGUgc2FtZSBmb2xkZXIsIHByZWZlcmFibHkgeW91ciBjbGFzcyBmb2xkZXIuIA0KDQpCZSBzdXJlIHRoZSBgaXB1bXNyYCBwYWNrYWdlIGlzIGluc3RhbGxlZC4gDQoNCmBgYHtyfQ0KbGlicmFyeShpcHVtc3IpDQpkZGkgPC0gcmVhZF9pcHVtc19kZGkoIkM6L1VzZXJzL296ZDUwNC8vT25lRHJpdmUgLSBVbml2ZXJzaXR5IG9mIFRleGFzIGF0IFNhbiBBbnRvbmlvLy9naXNfY2xhc3N3b3JrL3VzYV8wMDA4My54bWwiKQ0KZGF0YSA8LSByZWFkX2lwdW1zX21pY3JvKGRkaSkNCmRhdGE8LWhhdmVuOjp6YXBfbGFiZWxzKGRhdGEpICNuZWNlc3NhcnkgdG8gYXZvaWQgcHJvYmxlbXMgd2l0aCAibGFiZWxsZWQiIGRhdGEgY2xhc3MNCm5hbWVzKGRhdGEpPC10b2xvd2VyKG5hbWVzKGRhdGEpKQ0KYGBgDQoNCiMjIExvYWQgc29tZSBvdGhlciBwYWNrYWdlcw0KYGBge3IsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KHN1cnZleSwgcXVpZXRseSA9IFQpDQpsaWJyYXJ5KHRpZHl2ZXJzZSwgcXVpZXRseSA9IFQpDQpsaWJyYXJ5KGNhciwgcXVpZXRseSA9IFQpDQpsaWJyYXJ5KGdncGxvdDIsIHF1aWV0bHkgPSBUKQ0KbGlicmFyeSh0aWdyaXMsIHF1aWV0bHkgPSBUKQ0KbGlicmFyeShjbGFzc0ludCwgcXVpZXRseSA9IFQpDQpsaWJyYXJ5KHRtYXAsIHF1aWV0bHkgPSBUKQ0KDQpgYGANCg0KDQojIyMgRG93bmxvYWQgZ2VvZ3JhcGhpYyBkYXRhIGZvciBQdWJsaWMgVXNlIE1pY3JvZGF0YSBBcmVhcw0KVGhlIFB1YmxpYyBVc2UgTWljcm9kYXRhIEFyZWEgaXMgdGhlIGxvd2VzdCBsZXZlbCBvZiBnZW9ncmFwaHkgaW4gdGhlIFBVTVMgZGF0YS4gVGhleSBjb3JyZXNwb25kIHRvIGdyZW9ncmFwaGljIGFyZXMgb2YgfiAxMDAsMDAwIHBlb3BsZS4gDQoNCmBgYHtyfQ0Kb3B0aW9ucyh0aWdyaXNfY2xhc3MgPSAic2YiKQ0KcHVtYXM8LXB1bWFzKHN0YXRlID0gIlRYIiwNCiAgICAgICAgICAgICB5ZWFyID0gMjAxOCwNCiAgICAgICAgICAgICBjYiA9IFQpDQoNCg0KcGxvdChwdW1hc1siR0VPSUQxMCJdLA0KICAgICBtYWluID0gIlB1YmxpYyBVc2UgTWljcm9kYXRhIEFyZWFzIGluIFRleGFzIikNCg0KDQptYXB2aWV3OjptYXB2aWV3KHB1bWFzLCB6Y29sPSAiR0VPSUQxMCIpDQpgYGANCg0KDQojIyBQcmVwYXJlIHZhcmlhYmxlcw0KSGVyZSBJIHJlY29kZSBzZXZlcmFsIGRlbW9ncmFwaGljIHZhcmlhYmxlcw0KDQpgYGB7cn0NCg0KZGF0YSRwd3QgPC0gZGF0YSRwZXJ3dA0KZGF0YSRod3QgPC0gZGF0YSRoaHd0DQoNCiNyYWNlL2V0aG5pY2l0eQ0KZGF0YSRoaXNwIDwtIFJlY29kZShkYXRhJGhpc3BhbiwgcmVjb2RlcyA9ICI5PU5BOyAxOjQ9J0hpc3BhbmljJzsgMD0nTm9uSGlzcGFuaWMnIikNCmRhdGEkcmFjZV9yZWMgPC0gUmVjb2RlKGRhdGEkcmFjZSwgcmVjb2RlcyA9ICIxPSdXaGl0ZSc7IDI9J0JsYWNrJzsgMz0nT3RoZXInOyA0OjY9J0FzaWFuJzsgNzo5PSdPdGhlciciKQ0KZGF0YSRyYWNlX2V0aCA8LSBpbnRlcmFjdGlvbihkYXRhJGhpc3AsIGRhdGEkcmFjZV9yZWMsIHNlcCA9ICJfIikNCmRhdGEkcmFjZV9ldGggIDwtIGFzLmZhY3RvcihpZmVsc2Uoc3Vic3RyKGFzLmNoYXJhY3RlcihkYXRhJHJhY2VfZXRoKSwxLDgpID09ICJIaXNwYW5pYyIsICJIaXNwYW5pYyIsIGFzLmNoYXJhY3RlcihkYXRhJHJhY2VfZXRoKSkpDQpkYXRhJHJhY2VfZXRoIDwtIHJlbGV2ZWwoZGF0YSRyYWNlX2V0aCwgcmVmID0gIk5vbkhpc3BhbmljX1doaXRlIikNCg0KI3NleA0KZGF0YSRtYWxlIDwtIGlmZWxzZShkYXRhJHNleCA9PSAxLDEsMCkNCg0KI2VkdWNhdGlvbg0KZGF0YSRlZHVjX2xldmVsPC0gUmVjb2RlKGRhdGEkZWR1Y2QsIHJlY29kZXMgPSAiMjo2MT0nMExUX0hTJzs2Mjo2ND0nMV9IU0QvR0VEJzs2NTo4MD0nMl9zb21lY29sbCc7OTA6MTAwPScyX3NvbWVjb2xsJzsgODE6ODM9JzNfQXNzb2NEZWdyZWUnOzEwMT0nNF9iYWNoZWxvcmRlZ3JlZSc7IDExMDoxMTY9JzRfQkFwbHVzX0dyYWREZWdyZWUnOyBlbHNlPU5BIikNCg0KI2VtcGxveW1lbnQNCmRhdGEkZW1wbG95ZWQgPC0gUmVjb2RlKGRhdGEkd3JrbHN0d2ssIHJlY29kZXMgPSAiMT0wOzI9MTsgZWxzZT1OQSIpDQoNCiNjaXRpemVuc2hpcA0KZGF0YSRjaXQ8LVJlY29kZShkYXRhJGNpdGl6ZW4sIHJlY29kZXMgPSAiMT0nVVMgYm9ybic7IDI9J25hdHVyYWxpemVkJzsgMzo0PSdub3RjaXRpemVuJztlbHNlPU5BICIpDQoNCiNpbmR1c3RyeQ0KZGF0YSRpbmRfZ3JvdXA8LVJlY29kZShkYXRhJGluZCwgcmVjb2RlcyA9ICIxNzA6NDkwPSdhZ19leHRyYWN0JzsgNzcwPSdjb25zdHJ1Y3Rpb24nOyAxMDcwOjM5OTA9J21hbnVmYWMnOyA0MDcwOjU3OTA9J3dob2xlX3JldGFpbCc7IDYwNzA6NjM5MD0ndHJhbnMnOyA2NDcwOjY3ODA9J2luZm9ybWF0aW9uJzsgNjg3MDo3MTkwPSAnZmlyZSc7IDcyNzA9Nzc5MD0ncHJvZi9zY2kvbWFuYWdlJzsgNzg2MDo4NDcwPSdlZHUvc29jaWFsJzsgODU2MDo4NjkwPSdhcnRzJzsgODc3MDo5MjkwPSdvdGhlcic7IDkzNzA6OTU5MD0ncHVibGljX2FkbSc7IDk2NzA6OTg3MD0nbWlsaXRhcnknOyBlbHNlPU5BICIpDQoNCmRhdGEkcHJvZnRlY2ggPC0gUmVjb2RlKGRhdGEkaW5kLCByZWNvZGVzID0gIjcyNzA6NzQ5MD0xOyAwPU5BOyBlbHNlPTAiKQ0KDQojYWdlIGluIDEwIHllYXIgaW50ZXJ2YWxzDQpkYXRhJGFnZWNhdDwtY3V0KGRhdGEkYWdlLCBicmVha3MgPSBjKDAsIDE4LCAyMCwgMzAsIDQwLCA1MCwgNjUsIDEyMCksIGluY2x1ZGUubG93ZXN0ID0gVCkNCg0KZGF0YSRpbmNvbWUgPC0gaWZlbHNlKGRhdGEkaW5jd2FnZT49OTk5OTk4LCBOQSwgZGF0YSRpbmN3YWdlKQ0KYGBgDQoNCg0KDQojIyBHZW5lcmF0ZSBzdXJ2ZXkgZGVzaWduIG9iamVjdA0KSGVyZSB3ZSBpZGVudGlmeSB0aGUgcGVyc29uIHdlaWdodHMgYW5kIHRoZSBzdXJ2ZXkgZGVzaWduIHZhcmlhYmxlcy4NCg0KYGBge3J9DQpkZXM8LXN2eWRlc2lnbihpZHMgPSB+Y2x1c3RlciwNCiAgICAgICAgICAgICAgIHN0cmF0YSA9IH4gc3RyYXRhLA0KICAgICAgICAgICAgICAgd2VpZ2h0cyA9IH5wd3QsDQogICAgICAgICAgICAgICBkYXRhID0gZGF0YSkNCmBgYA0KDQojIyBwZXJmb3JtIHN1cnZleSBlc3RpbWF0aW9uIGZvciBQVU1Bcw0KVGhlIGBzdnlieSgpYCBmdW5jdGlvbiBhbGxvd3MgdXMgY2FsY3VsYXRlIGVzdGltYXRlcyBmb3IgZGlmZmVyZW50ICoqc3ViLWRvbWFpbnMqKiB3aXRoaW4gdGhlIGRhdGEsIHRoaXMgY291bGQgYmUgYSBkZW1vZ3JhcGhpYyBjaGFyYWN0ZXJpc3RpYywgYnV0IHdlJ2xsIHVzZSBvdXIgZ2VvZ3JhcGhpYyBsZXZlbC4gT2YgY291cnNlIHlvdSBjb3VsZCBkbyBib3RoLi4uLiANCg0KYGBge3J9DQp0ZXN0PC1zdnl0YWJsZSh+SShjaXQ9PSJVUyBib3JuIikrcHVtYStzZXgsIGRlc2lnbj1kZXMgKQ0KDQpwdW1hX2VzdF9lZHU8LXN2eWJ5KGZvcm11bGEgPSB+ZWR1Y19sZXZlbCwNCiAgICAgICAgICAgICAgICAgICAgYnkgPSB+cHVtYSwNCiAgICAgICAgICAgICAgICAgICAgZGVzaWduID0gc3Vic2V0KGRlcywgYWdlPjI1KSwNCiAgICAgICAgICAgICAgICAgICAgRlVOPXN2eW1lYW4sDQogICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSApDQoNCnB1bWFfZXN0X2VtcGxveTwtc3Z5YnkoZm9ybXVsYSA9IH5lbXBsb3llZCwNCiAgICAgICAgICAgICAgICAgICAgICAgYnkgPSB+cHVtYSwNCiAgICAgICAgICAgICAgICAgICAgICAgZGVzaWduPXN1YnNldChkZXMsIGFnZSAlaW4lIDE4OjY1KSwNCiAgICAgICAgICAgICAgICAgICAgICAgRlVOPXN2eW1lYW4sDQogICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSApDQoNCnB1bWFfZXN0X2luZHVzdHJ5PC1zdnlieShmb3JtdWxhID0gfnByb2Z0ZWNoLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gfnB1bWErc2V4K3JhY2VfZXRoLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiA9IHN1YnNldChkZXMsIGVtcGxveWVkPT0xKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBGVU4gPSBzdnltZWFuLA0KICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSApDQpgYGANCg0KDQpgYGB7cn0NCmxpYnJhcnkocmVsZGlzdCkNCmdpbmkucHVtYTwtZGF0YSU+JQ0KICBmaWx0ZXIoaW5jb21lID4wLCBpcy5uYShpbmNvbWUpPT1GKSU+JQ0KICBncm91cF9ieSggcHVtYSwgcmFjZV9ldGgpJT4lDQogIHN1bW1hcml6ZShpbmVxID0gZ2luaShpbmNvbWUsIHdlaWdodHMgPSBwd3QpKSU+JQ0KICB1bmdyb3VwKCkNCmBgYA0KDQoNCmBgYHtyfQ0KaGVhZChwdW1hX2VzdF9lZHUpDQpoZWFkKHB1bWFfZXN0X2VtcGxveSkNCmhlYWQocHVtYV9lc3RfaW5kdXN0cnkpDQpgYGANCg0KIyMgam9pbiB0byBnZW9ncmFwaHkNCg0KYGBge3J9DQpwdW1hcyRwdW1hPC1hcy5udW1lcmljKHB1bWFzJFBVTUFDRTEwKQ0KDQpnZW8xPC1sZWZ0X2pvaW4ocHVtYXMsIHB1bWFfZXN0X2VtcGxveSwgYnk9YygicHVtYSI9ICJwdW1hIikpDQpoZWFkKGdlbzEpDQoNCmdlbzI8LWxlZnRfam9pbihwdW1hcywgcHVtYV9lc3RfaW5kdXN0cnksIGJ5PWMoInB1bWEiPSAicHVtYSIpKQ0KaGVhZChnZW8yKQ0KDQpnZW8zPC1sZWZ0X2pvaW4ocHVtYXMsIGdpbmkucHVtYSwgYnk9YygicHVtYSI9ICJwdW1hIikpDQpoZWFkKGdlbzIpDQoNCmBgYA0KDQojIyBNYXAgZXN0aW1hdGVzDQoNCiMjIyBFbXBsb3ltZW50IHJhdGVzIGJ5IFBVTUENCmBgYHtyfQ0KDQp0bWFwX21vZGUoInZpZXciKQ0KDQpnZW8xJT4lDQogIHRtX3NoYXBlKCkrDQogIHRtX3BvbHlnb25zKCJlbXBsb3llZCIsDQogICAgICAgICAgICAgIHN0eWxlPSJrbWVhbnMiLA0KICAgICAgICAgICAgICBuPTgsDQogICAgICAgICAgICAgIGxlZ2VuZC5oaXN0ID0gVFJVRSkgKw0KICB0bV9sYXlvdXQobGVnZW5kLm91dHNpZGUgPSBUUlVFLA0KICAgICAgICAgICAgdGl0bGUgPSAiRW1wbG95bWVudCByYXRlIGluIFRleGFzIFBVTUFzIFxuIDIwMTQtMjAxOCIpIA0KDQoNCmBgYA0KDQpgYGB7cn0NCiN0bWFwX21vZGUoInBsb3QiKQ0KZ2VvMiU+JQ0KICB0bV9zaGFwZSgpKw0KICB0bV9wb2x5Z29ucygicHJvZnRlY2giLA0KICAgICAgICAgICAgICBzdHlsZT0ia21lYW5zIiwNCiAgICAgICAgICAgICAgbj04LA0KICAgICAgICAgICAgICBsZWdlbmQuaGlzdCA9IFRSVUUpICsNCiB0bV9sYXlvdXQobGVnZW5kLm91dHNpZGUgPSBUUlVFLA0KICAgICAgICAgICAgdGl0bGUgPSAiRW1wbG95bWVudCByYXRlIGluIFRleGFzIFBVTUFzIFxuIDIwMTQtMjAxOCIpICANCg0KYGBgDQoNCmBgYHtyfQ0KICANCmdlbzMlPiUNCnRtX3NoYXBlKCkrDQogIHRtX3BvbHlnb25zKCJpbmVxIiwNCiAgICAgICAgICAgICAgc3R5bGU9ImttZWFucyIsDQogICAgICAgICAgICAgIG49OCwNCiAgICAgICAgICAgICAgbGVnZW5kLmhpc3QgPSBUUlVFKSArDQogdG1fbGF5b3V0KGxlZ2VuZC5vdXRzaWRlID0gVFJVRSwNCiAgICAgICAgICAgIHRpdGxlID0gIkVtcGxveW1lbnQgcmF0ZSBpbiBUZXhhcyBQVU1BcyBcbiAyMDE0LTIwMTgiKSAgDQoNCmBgYA0KDQoNCiMjIEVzdGltYXRpb24gZm9yIG1ldHJvIGFyZWFzDQpIZXJlIHdlIHVzZSBjb3JlIGJhc2VkIHN0YXRpc3RpY2FsIGFyZWFzIGluc3RlYWQgb2YgUFVNQXMNCg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnfQ0KbWV0czwtY29yZV9iYXNlZF9zdGF0aXN0aWNhbF9hcmVhcyhjYiA9IFQsIHllYXIgPSAyMDE4KQ0KbWV0czwtbWV0c1tncmVwKG1ldHMkTkFNRSxwYXR0ZXJuID0gICJUWCIpLF0NCnBsb3QobWV0c1siTkFNRSJdKQ0KDQpzdHM8LXN0YXRlcyhjYj1ULCB5ZWFyPTIwMTgpDQpzdHM8LXN0cyU+JQ0KICBmaWx0ZXIoR0VPSUQ9PTQ4KQ0KYGBgDQojIyBlc3RpbWF0ZXMgYnkgbWV0cm8gYXJlYQ0KYGBge3J9DQptZXRfZXN0X2VkdTwtc3Z5YnkoZm9ybXVsYSA9IH5lZHVjX2xldmVsLA0KICAgICAgICAgICAgICAgICAgIGJ5ID0gfm1ldDIwMTMsDQogICAgICAgICAgICAgICAgICAgZGVzaWduPXN1YnNldChkZXMsYWdlPjI1KSwNCiAgICAgICAgICAgICAgICAgICBGVU49c3Z5bWVhbiwNCiAgICAgICAgICAgICAgICAgICBuYS5ybT1UICkNCg0KbWV0X2VzdF9lbXBsb3k8LXN2eWJ5KGZvcm11bGEgPSB+ZW1wbG95ZWQsDQogICAgICAgICAgICAgICAgICAgICAgYnkgPSB+bWV0MjAxMywNCiAgICAgICAgICAgICAgICAgICAgICBkZXNpZ249c3Vic2V0KGRlcywgYWdlJWluJTE4OjY1KSwNCiAgICAgICAgICAgICAgICAgICAgICBGVU49c3Z5bWVhbiwNCiAgICAgICAgICAgICAgICAgICAgICBuYS5ybT1UICkNCg0KbWV0X2VzdF9pbmR1c3RyeTwtc3Z5YnkoZm9ybXVsYSA9IH5wcm9mdGVjaCwNCiAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gfm1ldDIwMTMsDQogICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ249c3Vic2V0KGRlcywgZW1wbG95ZWQ9PTEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgRlVOPXN2eW1lYW4sDQogICAgICAgICAgICAgICAgICAgICAgICBuYS5ybT1UICkNCg0KaGVhZChtZXRfZXN0X2VkdSkNCmhlYWQobWV0X2VzdF9lbXBsb3kpDQpoZWFkKG1ldF9lc3RfaW5kdXN0cnkpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KbWV0cyRtZXQyMDEzPC1hcy5udW1lcmljKG1ldHMkR0VPSUQpDQpnZW8zPC1sZWZ0X2pvaW4obWV0cywgbWV0X2VzdF9lbXBsb3ksYnk9YygibWV0MjAxMyI9ICJtZXQyMDEzIikpDQoNCmBgYA0KDQpOb3RlLCBncmV5IE1ldHJvcyBhcmUgb25lcyB0aGF0IGFyZSBub3QgaWRlbnRpZmllZCBpbiB0aGUgQUNTDQpgYGB7cn0NCnRtYXBfbW9kZSgidmlldyIpDQoNCmdlbzMlPiUNCiAgdG1fc2hhcGUoKSsNCiAgdG1fcG9seWdvbnMoImVtcGxveWVkIiwNCiAgICAgICAgICAgICAgc3R5bGU9ImttZWFucyIsDQogICAgICAgICAgICAgIG49OCwNCiAgICAgICAgICAgICAgbGVnZW5kLmhpc3QgPSBUUlVFKSArDQogdG1fbGF5b3V0KGxlZ2VuZC5vdXRzaWRlID0gVFJVRSwNCiAgICAgICAgICAgIHRpdGxlID0gIkVtcGxveW1lbnQgcmF0ZSBpbiBUZXhhcyBNZXRybyBBcmVhcyBcbiAyMDE0LTIwMTgiKSAgDQoNCmBgYA0KDQoNCiMjIEVzdGltYXRpb24gZm9yIENvdW50aWVzDQojIyANCmBgYHtyLCByZXN1bHRzPSdoaWRlJ30NCmNvczwtY291bnRpZXMoY2I9IFQsc3RhdGUgPSAiVFgiLCB5ZWFyID0gMjAxOCkNCnBsb3QoY29zWyJOQU1FIl0pDQoNCnN0czwtc3RhdGVzKGNiPVQsIHllYXI9MjAxOCkNCnN0czwtc3RzJT4lDQogIGZpbHRlcihHRU9JRD09NDgpDQpgYGANCiMjIGVzdGltYXRlcyBieSBjb3VudHkgYXJlYQ0KYGBge3J9DQpjb3NfZXN0X2VkdTwtc3Z5YnkoZm9ybXVsYSA9IH5lZHVjX2xldmVsLA0KICAgICAgICAgICAgICAgICAgIGJ5ID0gfmNvdW50eWZpcCwNCiAgICAgICAgICAgICAgICAgICBkZXNpZ249c3Vic2V0KGRlcyxhZ2U+MjUpLA0KICAgICAgICAgICAgICAgICAgIEZVTj1zdnltZWFuLCBuYS5ybT1UICkNCmNvc19lc3RfZW1wbG95PC1zdnlieShmb3JtdWxhID0gfmVtcGxveWVkLA0KICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gfmNvdW50eWZpcCwNCiAgICAgICAgICAgICAgICAgICAgICBkZXNpZ249c3Vic2V0KGRlcywgYWdlJWluJTE4OjY1KSwNCiAgICAgICAgICAgICAgICAgICAgICBGVU49c3Z5bWVhbiwgbmEucm09VCApDQpjb3NfZXN0X2luZHVzdHJ5PC1zdnlieShmb3JtdWxhID0gfnByb2Z0ZWNoLA0KICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSB+Y291bnR5ZmlwLA0KICAgICAgICAgICAgICAgICAgICAgICAgZGVzaWduPXN1YnNldChkZXMsIGVtcGxveWVkPT0xKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIEZVTj1zdnltZWFuLCBuYS5ybT1UICkNCg0KaGVhZChjb3NfZXN0X2VkdSkNCmhlYWQoY29zX2VzdF9lbXBsb3kpDQpoZWFkKGNvc19lc3RfaW5kdXN0cnkpDQoNCmBgYA0KQWdhaW4sIHRoZSBBQ1MgZG9lc24ndCBpZGVudGlmeSBjb3VudGllcyBpbiB0aGUgbWljcm9kYXRhIGV4Y2VwdCBmb3IgdGhvc2UgY291bnRpZXMgd2l0aCBzbWFsbCBwb3B1bGF0aW9ucy4gVGhlIGxpc3Qgb2YgaWRlbnRpZmllZCBjb3VudGllcyBjYW4gYmUgZm91bmQgW2hlcmVdKGh0dHBzOi8vdXNhLmlwdW1zLm9yZy91c2EtYWN0aW9uL3ZhcmlhYmxlcy9DT1VOVFlGSVAjY29kZXNfc2VjdGlvbikNCg0KYGBge3J9DQpjb3MkY29maXA8LWFzLm51bWVyaWMoY29zJENPVU5UWUZQKQ0KDQoNCmdlbzQ8LWxlZnRfam9pbihjb3MsIGNvc19lc3RfZW1wbG95LGJ5PWMoImNvZmlwIj0gImNvdW50eWZpcCIpKQ0KDQp0bWFwX21vZGUoInZpZXciKQ0KDQpnZW80JT4lDQogIHRtX3NoYXBlKCkrDQogIHRtX3BvbHlnb25zKCJlbXBsb3llZCIsDQogICAgICAgICAgICAgIHN0eWxlPSJrbWVhbnMiLA0KICAgICAgICAgICAgICBuPTgsDQogICAgICAgICAgICAgIGxlZ2VuZC5oaXN0ID0gVFJVRSkgKw0KIHRtX2xheW91dChsZWdlbmQub3V0c2lkZSA9IFRSVUUsDQogICAgICAgICAgICB0aXRsZSA9ICJFbXBsb3ltZW50IHJhdGUgaW4gVGV4YXMgQ291bnRpZXMgXG4gMjAxNC0yMDE4IikgIA0KDQpgYGANCg0KDQo=